﻿using System;
using System.Collections.Generic;
using System.Text;
using ESRI.ArcGIS.Geodatabase;
using System.Diagnostics;

namespace CanSWAT.ArcSWAT
{
    /// <summary>
    /// corresponding to table hrus
    /// </summary>
    class Subbasin
    {
        public Subbasin(IRow row)
        {
            _id = Convert.ToInt32(row.get_Value(SUBBAIN_ID_INDEX).ToString());
            _area = Convert.ToDouble(row.get_Value(SUBBAIN_AREA_INDEX).ToString());
        }

        public Subbasin(int id, double area)
        {
            _id = id;
            _area = area;
        }

        public static int SUBBAIN_ID_INDEX = 1;
        public static int SUBBAIN_AREA_INDEX = 2;

        private int _id = -1;
        private double _area = 0.0;

        private List<HRU> _hrus = null;
        private double _modeledHRUArea = 0.0;

        public int ID
        {
            get { return _id; }
        }

        public void AddHRU(HRU hru)
        {
            if (_hrus == null)
            {
                _hrus = new List<HRU>();
                _modeledHRUArea = 0.0;
            }

            _hrus.Add(hru);
            _modeledHRUArea += hru.Area;
        }

        public double Area_ModeldHRUs
        {
            get { return _modeledHRUArea; }
        }

        public List<HRU> HRUs
        {
            get { return _hrus; }
        }

        #region Elevation Band

        private List<int> _cellElevations = null;
        private List<ElevationBand> _elevationBands = null;
        private bool _isCellElevationsSorted = false;
        private static string FIELD_NAME_BAND_ELEVATION = "ELEVB";
        private static string FIELD_NAME_BAND_FRACTION = "ELEVB_FR";
        private static string FIELD_NAME_BAND_INITIAL_SNOW = "SNOEB";

        public List<ElevationBand> Bands
        {
            get { return _elevationBands; }
        }

        /// <summary>
        /// Add one cell elevation to the collection
        /// </summary>
        /// <param name="elev"></param>
        public void AddElevation(int elev)
        {            
            if (_cellElevations == null) 
                _cellElevations = new List<int>();

            _cellElevations.Add(elev);
        }

        /// <summary>
        /// creat all the elevation bands based given options
        /// </summary>
        /// <param name="method"></param>
        /// <param name="numOfBand"></param>
        /// <param name="initialSnow"></param>
        public void createElevationBands(ElevationBandMethodType method, int numOfBand, double initialSnow)
        {
            if (_elevationBands == null) 
                _elevationBands  = new List<ElevationBand>();
            _elevationBands.Clear();


            if (!_isCellElevationsSorted)
            {
                _cellElevations.Sort();
                _isCellElevationsSorted = true;
            }

            
            int numOfCell = _cellElevations.Count;
            if (method == ElevationBandMethodType.EqualElevation)
            {
                int elevationInterval = (_cellElevations[numOfCell - 1] - _cellElevations[0]) / numOfBand + 1;
                int currentElevationLimit = _cellElevations[0] + elevationInterval;
                int currentNumOfCell = 0;
                int currentTotalElevation = 0;

                foreach (int elev in _cellElevations)
                {
                    if (elev <= currentElevationLimit)
                    {
                        currentTotalElevation += elev;
                        currentNumOfCell++;
                    }
                    else
                    {
                        _elevationBands.Add(
                            new ElevationBand(
                                Convert.ToSingle(currentTotalElevation) / Convert.ToSingle(currentNumOfCell),      //average elevation
                                Convert.ToSingle(currentNumOfCell) / Convert.ToSingle(numOfCell), //area fraction
                                Convert.ToSingle(initialSnow)));

                        currentElevationLimit += elevationInterval;
                        currentNumOfCell = 0;
                        currentTotalElevation = 0;
                    }
                }

                //the last band
                if(currentNumOfCell > 0)
                    _elevationBands.Add(
                            new ElevationBand(
                                Convert.ToSingle(currentTotalElevation) / Convert.ToSingle(currentNumOfCell),      //average elevation
                                Convert.ToSingle(currentNumOfCell) / Convert.ToSingle(numOfCell), //area fraction
                                Convert.ToSingle(initialSnow)));
            }
            else
            {
                int numInterval = numOfCell / numOfBand + 1;

                int currentNumLimit = numInterval;
                int currentNumOfCell = 0;
                int currentTotalElevation = 0;

                for (int i = 0; i < numOfCell; i++)
                {
                    if (i < currentNumLimit)
                    {
                        currentNumOfCell++;
                        currentTotalElevation += _cellElevations[i];
                    }
                    else
                    {
                        _elevationBands.Add(
                            new ElevationBand(
                                Convert.ToSingle(currentTotalElevation) / Convert.ToSingle(currentNumOfCell),      //average elevation
                                Convert.ToSingle(currentNumOfCell) / Convert.ToSingle(numOfCell), //area fraction
                                Convert.ToSingle(initialSnow)));

                        currentNumLimit += numInterval;
                        currentNumOfCell = 0;
                        currentTotalElevation = 0;
                    }
                }
                //the last band
                if (currentNumOfCell > 0)
                    _elevationBands.Add(
                        new ElevationBand(
                            Convert.ToSingle(currentTotalElevation) / Convert.ToSingle(currentNumOfCell),      //average elevation
                            Convert.ToSingle(currentNumOfCell) / Convert.ToSingle(numOfCell), //area fraction
                            Convert.ToSingle(initialSnow)));
            }

            CanSWAT.debugInformation(string.Format("{0} elevation bands are generated in subbasin {1}",_elevationBands.Count, ID));
        }

        /// <summary>
        /// Save elevation bands into a row of table sub
        /// </summary>
        /// <param name="r"></param>
        public void saveElevationBands2row(IRow r)
        {
            if (_elevationBands == null)
                throw new System.Exception("Please generate elevation band first.");           

            int num = _elevationBands.Count;
            if(num == 0)
                throw new System.Exception("No elevation band.");

            string format = "{0}{1}";
            for (int i = 0; i < _elevationBands.Count; i++)
            {
                saveElevationBands2row(
                    r,
                    string.Format(format, FIELD_NAME_BAND_ELEVATION, i + 1),
                    _elevationBands[i].Elevation);

                saveElevationBands2row(
                    r,
                    string.Format(format, FIELD_NAME_BAND_FRACTION, i + 1),
                    _elevationBands[i].Fraction);

                saveElevationBands2row(
                    r,
                    string.Format(format, FIELD_NAME_BAND_INITIAL_SNOW, i + 1),
                    _elevationBands[i].IntialSnow);
            }
        }

        private void saveElevationBands2row(IRow r, string fieldName, float fieldValue)
        {
            int index = r.Fields.FindField(fieldName);

            if (index == -1)
                throw new System.Exception("Can't find field " + fieldName);

            r.set_Value(index, fieldValue);
        }

        #endregion


    }
}
